#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <unistd.h>

#include <err.h>
#include <sysexits.h>

#include "au.h"
#include "common.h"

void
auconvp(struct au *au, uint32_t o, void *b, size_t s)
{
	char *argv[8];
	size_t i = 0;
	int t;

	if (au == NULL || b == NULL) {
		errno = EFAULT;
		return;
	}
	errno = E2BIG;
	argv[0] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "auconv")) < 0 ||
	    (i += t + 1) >= s)
		return;
	argv[1] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-j%"PRIu32,
	    au->au_off)) < 0 || (i += t + 1) >= s)
		return;
	argv[2] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-s%"PRIu32,
	    au->au_size)) < 0 || (i += t + 1) >= s)
		return;
	argv[3] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-i%"PRIu32,
	    au->au_enc)) < 0 || (i += t + 1) >= s)
		return;
	argv[4] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-r%"PRIu32,
	    au->au_rate)) < 0 || (i += t + 1) >= s)
		return;
	argv[5] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-c%"PRIu32,
	    au->au_chan)) < 0 || (i += t + 1) >= s)
		return;
	argv[6] = (char *)b + i;
	if ((t = snprintf((char *)b + i, s - i, "-o%"PRIu32, o)) < 0 ||
	     (i += t + 1) >= s)
		return;
	argv[7] = NULL;
	(void)execvp("auconv", argv);
}

ssize_t
a_lookup(const struct a_lookup *stab, const char *s, int *val)
{
	size_t i;
	size_t j;

	if (stab == NULL || s == NULL)
		ERR(EFAULT, -1);
	for (i = 0; isspace(s[i]); i++)
		/* do nothing */;
	s += i;
	for (j = 0; s[j] != '\0' && !isspace(s[j]); j++)
		/* do nothing */;
	if (j > 0)
		for (; stab->string != NULL; stab++)
			if (strncasecmp(s, stab->string, j) == 0) {
				*val = stab->value;
				return (i + j);
			}
	*val = 0;
	return (0);
}

int
setblock(int fd, int block)
{
	int f;

	if ((f = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL,
	    block ? (f & ~O_NONBLOCK) : (f | O_NONBLOCK)) == -1)
		return (-1);
	return (0);
}

uint32_t
get4(const void *p)
{
	const uint8_t *u = p;

	return (((uint32_t)u[0] << 24) | ((uint32_t)u[1] << 16) |
	    ((uint32_t)u[2] << 8) | (uint32_t)u[3]);
}

void
put4(void *p, uint32_t v)
{
	uint8_t *u = p;

	u[0] = v >> 24;
	u[1] = v >> 16;
	u[2] = v >> 8;
	u[3] = v >> 0;
}

ssize_t
nread(int fd, void *b, size_t s)
{
	size_t i;
	ssize_t t;

	if (b == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if (s > SSIZE_MAX) {
		errno = ERANGE;
		return (-1);
	}
	for (i = 0; i < s; i += t)
		if ((t = read(fd, (char *)b + i, s - i)) < 0)
			return (t);
		else if (t == 0)
			return (i);
	return (s);
}

ssize_t
nwrite(int fd, const void *b, size_t s)
{
	size_t i;
	ssize_t t;

	if (b == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if (s > SSIZE_MAX) {
		errno = ERANGE;
		return (-1);
	}
	for (i = 0; i < s; i += t)
		if ((t = write(fd, (const char *)b + i, s - i)) < 0)
			return (t);
		else if (t == 0)
			return (i);
	return (s);
}

size_t
szwrite(char *out, size_t s, const char *suf, const char *sufs)
{
	const char suffix[] = {
		0, 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
	};
	char b[6];
	size_t i;

	if (suf == NULL && sufs != NULL)
		suf = sufs;
	else if (suf != NULL && sufs == NULL)
		sufs = suf;
	for (i = 0; i < sizeof(suffix) && s > 9999;
	    i++, s /= 1024)
		/* do nothing */;
	b[5] = suffix[i];
	if (s > 9999) {
		b[0] = '>';
		b[1] = b[2] = b[3] = b[4] = '9';
		i = 0;
	} else if (s == 0)
		b[i--] = '0';
	else
		for (i = 4; i > 0 && s > 0; i--, s /= 10)
			b[i] = s % 10 + '0';
	i++;
	s = sizeof(b) - i;
	(void)memcpy(out, b + i, s);
	if (b[5])
		suf = sufs;
	if (suf != NULL) {
		i = strlen(suf);
		(void)memcpy(out + s, suf, i);
		s += i;
	}
	return (s);
}

size_t
strtosz(const char *str)
{
	unsigned long v;
	char *cp;

	errno = 0;
	v = strtoul(str, &cp, 0);
	if (cp == str || *cp != '\0')
		errx(EX_USAGE, "Invalid number '%s'", str);
	if (v > SIZE_MAX)
		errno = ERANGE;
	if (errno == ERANGE)
		err(EX_USAGE, "Invalid number '%s'", str);
	return (v);
}

uint32_t
strtou32(const char *str)
{
	unsigned long v;
	char *cp;

	errno = 0;
	v = strtoul(str, &cp, 0);
	if (cp == str || *cp != '\0')
		errx(EX_USAGE, "Invalid number '%s'", str);
	if (v > UINT32_MAX)
		errno = ERANGE;
	if (errno == ERANGE)
		err(EX_USAGE, "Invalid number '%s'", str);
	return (v);
}

ssize_t
memacat(void **dst, size_t *dsts, void *src, size_t srcs)
{
	void *p;

	if (dst == NULL || dsts == NULL || src == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if (srcs == 0)
		return (0);
	if ((p = realloc(*dst, *dsts + srcs)) == NULL)
		return (-1);
	*dst = p;
	(void)memcpy((void *)((uintptr_t)p + *dsts), src, srcs);
	*dsts += srcs;
	return (srcs);
}
